import * as sinon from 'sinon';
import * as chai from 'chai';
import proxyquire from 'proxyquire';
import {IParams} from "../src/params";

const expect = chai.expect;

// set locale to zh_CN in global
process.env["PIPELINE_LOCALE"] = 'zh_CN';

describe('copyToTargetBin', () => {
    let fsStub: any;
    let stepStub: any;
    let copyToTargetBinProxy: any;

    beforeEach(() => {
        fsStub = {
            existsSync: sinon.stub(),
            unlinkSync: sinon.stub(),
            copyFileSync: sinon.stub(),
            statSync: sinon.stub(),
            chmodSync: sinon.stub()
        };

        stepStub = {
            debug: sinon.stub()
        };

        const utilModule = proxyquire('../src/util', {
            fs: fsStub,
            '@flow-step/step-toolkit': stepStub
        });

        copyToTargetBinProxy = utilModule.copyToTargetBin;
    });

    afterEach(() => {
        sinon.restore();
    });

    it('should copy file and set permissions to 755 if not already set', () => {
        const sourceBinFile = '/path/to/source.bin';
        const targetBinFile = '/path/to/target.bin';

        fsStub.existsSync.returns(true);
        fsStub.statSync.returns({mode: 0o644});

        const result = copyToTargetBinProxy(sourceBinFile, targetBinFile);
        expect(result).to.equal(targetBinFile);
        expect(fsStub.existsSync.calledWith(targetBinFile)).to.be.true;
        expect(fsStub.unlinkSync.calledWith(targetBinFile)).to.be.true;
        expect(fsStub.copyFileSync.calledWith(sourceBinFile, targetBinFile)).to.be.true;
        expect(fsStub.statSync.calledWith(targetBinFile)).to.be.true;
        expect(fsStub.chmodSync.calledWith(targetBinFile, 0o755)).to.be.true;
    });

    it('should not change permissions if they are already 755', () => {
        const sourceBinFile = '/path/to/source.bin';
        const targetBinFile = '/path/to/target.bin';

        fsStub.existsSync.returns(false);
        fsStub.statSync.returns({mode: 0o755});

        const result = copyToTargetBinProxy(sourceBinFile, targetBinFile);
        expect(result).to.equal(targetBinFile);
        expect(fsStub.existsSync.calledWith(targetBinFile)).to.be.true;
        expect(fsStub.unlinkSync.called).to.be.false;
        expect(fsStub.copyFileSync.calledWith(sourceBinFile, targetBinFile)).to.be.true;
        expect(fsStub.statSync.calledWith(targetBinFile)).to.be.true;
        expect(fsStub.chmodSync.called).to.be.false;
    });

    it('should throw an error if setting permissions fails', () => {
        const sourceBinFile = '/path/to/source.bin';
        const targetBinFile = '/path/to/target.bin';

        fsStub.existsSync.returns(false);
        fsStub.statSync.returns({mode: 0o644});
        fsStub.chmodSync.throws(new Error('Permission denied'));


        expect(() => copyToTargetBinProxy(sourceBinFile, targetBinFile)).to.throw('Error setting file permissions: Permission denied');
    });
});

describe('writeParamsToFile', () => {
    let fsStub: any;
    let writeParamsToFileProxy: any;

    beforeEach(() => {
        fsStub = {
            existsSync: sinon.stub(),
            mkdirSync: sinon.stub(),
            writeFileSync: sinon.stub()
        };

        const utilModule = proxyquire('../src/util', {
            fs: fsStub
        });

        writeParamsToFileProxy = utilModule.writeParamsToFile;
    });

    afterEach(() => {
        sinon.restore();
    });

    it('should create directory and write params to file if directory does not exist', () => {
        const params: IParams = {
            pipelineID: 123,
            pipelineName: 'test-pipeline',
            buildNumber: 456,
            workSpace: '/test/workspace',
            projectDir: '/test/project',
            buildJobID: 789,
            jenkinsMaster: 'http://master.example.com',
            jenkinsUsername: 'user',
            jenkinsPassword: 'pass',
            jenkinsJob: 'test-job',
            jenkinsBuildNumber: 12,
            params: 'test-params'
        };

        const tmpParamsDir = `/tmp/${params.buildJobID}`;
        const tmpParamsFile = `${tmpParamsDir}/params`;

        fsStub.existsSync.returns(false);

        const result = writeParamsToFileProxy(params);

        expect(result).to.equal(tmpParamsFile);
        expect(fsStub.existsSync.calledWith(tmpParamsDir)).to.be.true;
        expect(fsStub.mkdirSync.calledWith(tmpParamsDir, {recursive: true})).to.be.true;
        expect(fsStub.writeFileSync.calledWith(tmpParamsFile, params.params as string)).to.be.true;
    });
});

describe('processJenkinsBuildNumber', () => {
    let stepStub: any;
    let processJenkinsBuildNumberProxy: any;

    beforeEach(() => {
        stepStub = {
            errorV1: sinon.stub(),
            warnV1: sinon.stub(),
            infoV1: sinon.stub(),
            addOutput: sinon.stub(),
            interaction: {
                addLink: sinon.stub()
            }
        };

        // 使用 proxyquire 替换 step 模块
        const utilModule = proxyquire('../src/util', {
            '@flow-step/step-toolkit': stepStub
        });

        processJenkinsBuildNumberProxy = utilModule.processJenkinsBuildNumber;
    });

    afterEach(() => {
        sinon.restore();
    });

    it('should handle error case with 403 status', () => {
        const jenkinsBuildNumber = 'error: \n错误码:403,错误信息: Forbidden';
        const params: IParams = {
            pipelineID: 123,
            pipelineName: 'test-pipeline',
            buildNumber: 456,
            workSpace: '/test/workspace',
            projectDir: '/test/project',
            buildJobID: 789,
            jenkinsMaster: 'http://master.example.com',
            jenkinsUsername: 'user',
            jenkinsPassword: 'pass',
            jenkinsJob: 'test-job',
            jenkinsBuildNumber: 12,
            params: 'test-params'
        };
        const result = processJenkinsBuildNumberProxy(jenkinsBuildNumber, params);
        expect(result).to.equal(1);
        expect(stepStub.addOutput.called).to.be.false;
        expect(stepStub.interaction.addLink.called).to.be.false;
    });

    it('should handle success case', () => {
        const jenkinsBuildNumber = '123';
        const params: IParams = {
            pipelineID: 123,
            pipelineName: 'test-pipeline',
            buildNumber: 456,
            workSpace: '/test/workspace',
            projectDir: '/test/project',
            buildJobID: 789,
            jenkinsMaster: 'http://master.example.com',
            jenkinsUsername: 'user',
            jenkinsPassword: 'pass',
            jenkinsJob: 'test-job',
            jenkinsBuildNumber: 12,
            params: 'test-params'
        };

        const expectedLogMessage1 = `当前触发的流水线任务构建号为${jenkinsBuildNumber}`;
        const expectedLogMessage2 = `Jenkins 任务日志地址 ${params.jenkinsMaster}/job/${params.jenkinsJob}/${jenkinsBuildNumber}/console`;
        const expectedActionOutput = "ACTION=CHECK";
        const expectedBuildNumberOutput = `JENKINS_BUILD_NUMBER=${jenkinsBuildNumber}`;
        const expectedLinkText = "任务日志";
        const expectedLinkUrl = `${params.jenkinsMaster}/job/${params.jenkinsJob}/${jenkinsBuildNumber}/console`;

        const result = processJenkinsBuildNumberProxy(jenkinsBuildNumber, params);
        expect(result).to.equal(0);
        expect(stepStub.infoV1.calledWith(expectedLogMessage1)).to.be.true;
        expect(stepStub.infoV1.calledWith(expectedLogMessage2)).to.be.true;
        expect(stepStub.addOutput.calledTwice).to.be.true;
        expect(stepStub.addOutput.calledWith(expectedActionOutput)).to.be.true;
        expect(stepStub.addOutput.calledWith(expectedBuildNumberOutput)).to.be.true;
        expect(stepStub.interaction.addLink.calledWith(expectedLinkText, expectedLinkUrl)).to.be.true;
    });
});


describe('processBuildResult', () => {
    let stepStub: any;
    let processBuildResultProxy: any;

    beforeEach(() => {
        stepStub = {
            addOutput: sinon.stub(),
            errorV1: sinon.stub(),
            debug: sinon.stub(),
            interaction: {
                addLink: sinon.stub()
            }
        };

        processBuildResultProxy = proxyquire('../src/util', {
            '@flow-step/step-toolkit': stepStub
        }).processBuildResult;
    });

    afterEach(() => {
        sinon.restore();
    });

    it('should handle BUILDING state', () => {
        const result = processBuildResultProxy('BUILDING', {
            jenkinsBuildNumber: '123',
            jenkinsMaster: 'http://example.com',
            jenkinsJob: 'test-job'
        });

        expect(result).to.equal(0);
        expect(stepStub.addOutput.calledWith('MESSAGE=当前任务正在运行中')).to.be.true;
        expect(stepStub.addOutput.calledWith('ACTION=CHECK')).to.be.true;
        expect(stepStub.addOutput.calledWith('JENKINS_BUILD_NUMBER=123')).to.be.true;
        expect(stepStub.interaction.addLink.calledWith('任务日志', 'http://example.com/job/test-job/123/console')).to.be.true;
    });

    it('should handle ABORTED state', () => {
        const result = processBuildResultProxy('ABORTED', {
            jenkinsBuildNumber: '123',
            jenkinsMaster: 'http://example.com',
            jenkinsJob: 'test-job'
        });

        expect(result).to.equal(1);
        expect(stepStub.errorV1.calledWith('当前任务在Jenkins中被手动Aborted，当前任务失败')).to.be.true;
        expect(stepStub.addOutput.calledWith('ACTION=ON_FAIL')).to.be.true;
        expect(stepStub.addOutput.calledWith('RESULT=ABORTED')).to.be.true;
        expect(stepStub.interaction.addLink.calledWith('任务日志', 'http://example.com/job/test-job/123/console')).to.be.true;
    });

    it('should handle FAILURE state', () => {
        const result = processBuildResultProxy('FAILURE', {
            jenkinsBuildNumber: '123',
            jenkinsMaster: 'http://example.com',
            jenkinsJob: 'test-job'
        });

        expect(result).to.equal(1);
        expect(stepStub.errorV1.calledWith('当前任务在Jenkins中运行失败，当前任务失败')).to.be.true;
        expect(stepStub.addOutput.calledWith('ACTION=ON_FAIL')).to.be.true;
        expect(stepStub.addOutput.calledWith('RESULT=FAILURE')).to.be.true;
        expect(stepStub.interaction.addLink.calledWith('任务日志', 'http://example.com/job/test-job/123/console')).to.be.true;
    });

    it('should handle SUCCESS state', () => {
        const result = processBuildResultProxy('SUCCESS', {
            jenkinsBuildNumber: '123',
            jenkinsMaster: 'http://example.com',
            jenkinsJob: 'test-job'
        });

        expect(result).to.equal(0);
        expect(stepStub.debug.calledWith('当前任务运行成功')).to.be.true;
        expect(stepStub.addOutput.calledWith('ACTION=ON_SUCCESS')).to.be.true;
        expect(stepStub.addOutput.calledWith('RESULT=SUCCESS')).to.be.true;
        expect(stepStub.interaction.addLink.calledWith('任务日志', 'http://example.com/job/test-job/123/console')).to.be.true;
    });
});